package cn.wolfcode.rbac.springsecurity.config;

import cn.wolfcode.rbac.springsecurity.filter.JwtAuthenFilter;
import cn.wolfcode.rbac.springsecurity.service.impl.EmployeeDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true,securedEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    //注入自定义数据源
    @Autowired
    private EmployeeDetailsService employeeDetailsService;
    @Autowired
    private JwtAuthenFilter jwtAuthenFilter;


    /**
     * 配置拦截规则
     * @param http the {@link HttpSecurity} to modify
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //仅有csrf，关闭csrf攻击的防护
        http.csrf().disable();

        http.authorizeRequests()
                .antMatchers("/api/login","/api/logout","/api/code").permitAll()
                .antMatchers("/**").authenticated();

        //配置自定义过滤器，在UsernamePasswordAuthenticationFilter过滤前执行
        http.addFilterBefore(jwtAuthenFilter, UsernamePasswordAuthenticationFilter.class);
    }

    /**
     * 实例化认证器
     * @return
     * @throws Exception
     */
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    /**
     * 密码匹配器
     * @return
     */
    @Bean
    public PasswordEncoder passwordEncoder(){
        //先用密码不需要加密匹配校验
        return NoOpPasswordEncoder.getInstance();
        //密码加密匹配校验
//        return new BCryptPasswordEncoder();
    }

    /**
     * 配置认证器
     * @param auth the {@link AuthenticationManagerBuilder} to use
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(employeeDetailsService) //指定数据源
                .passwordEncoder(passwordEncoder()); //指定密码匹配器
    }
}

/*
 * 1.集成认证
 *   1.1 配置
 *    1.1.1 配置拦截规则
 *    1.1.2 自定义数据源 以及 封装我们查询的用户信息
 *    1.1.3 密码匹配器
 *    1.1.4 实例化认证器
 *    1.1.5 配置认证器的数据源和密码匹配器
 *   1.2 在业务层Service中调用认证器 实现认证
 *   1.3 自定义过滤器实现拦截每个请求所携带请求头的token,是否认证，如果认证就把员工信息放到上下文中
 *   1.4 在Security配置文件中，配置自定义认证器 在 userNameAndPasswordFilter过滤器前执行
 * 2.集成jwt
 *    2.1 封装工具类【创建token,根据token解析payload并返回】
 *    2.2 调用认证器后认证成功，则生成uuid,封装到token中，然后根据token写入employee信息到redis
 *    2.3 从自定义过滤器中获取token,并解析成uuid,再从redis中查处employee员工信息
 *      2.3.1 如果取出为空，表示未登录
 *      2.3.2 如果取出不为空，则表示登录，把employee的username和password封装成userNameAndPassword的token存入到上下文中
 * 3.集成授权
 *   3.1 在配置文件类上开启授权注解功能
 *   3.2 在controller层需要授权的方法加上注解
 *   3.3 在自定义数据源的返回对象中UserDetails的封装权限集。
 *      3.3.1 如果是超级管理员则返回null
 *      3.3.2 如果不是超级管理员则根据id从数据库中查询权限
 *   3.4 在controller层对注解进行优化，允许超级管理员
 *      3.4.1 自定义service中，在service中自定义判断授权的方法
 *      3.4.2 在授权注解上 用 @调用service中的授权方法
*/
